package main

import (
	"fmt"
	"rand"
	"sync"
	"time"
	)

type Executor interface {
	Execute() (ret int)
}

type StringPrintExecutor struct {
	message string
}

func NewStringPrintExecutor(in_message string) *StringPrintExecutor {
	return &StringPrintExecutor{message: in_message}
}

func (exec *StringPrintExecutor) Execute() (ret int) {
	time.Sleep(int64(rand.Intn(1000000) + 10000))
	fmt.Println(exec.message)
	return 0
}

type NotifyExecutor struct {
	done_chan chan int
}

func NewNotifyExecutor(c chan int) *NotifyExecutor {
	ret := new(NotifyExecutor)
	ret.done_chan = c
	return ret
}

func (exec *NotifyExecutor) Execute() (ret int) {
	exec.done_chan <- 1
	return 1
}

func main() {
	fmt.Println("starting")

	exec_channel := make(chan Executor)
	done_chan := make(chan int)

	// go func here.
	// This needs to be before when exec_channel is populated
	// since the channel is unbuffered, hence synchronous!
	go func() {
		fmt.Printf("Values in channel = %d\n", len(exec_channel))

		hg := new(sync.WaitGroup)
    // The iteration will go on until the channel is closed.
		for exec := range(exec_channel) {
			// It isn't safe to use exec - as there is a race.
			go func(ex Executor) {
				ex.Execute()
				hg.Done()
			}(exec)
			hg.Add(1)
		}
		hg.Wait()
		fmt.Println("Before emitting")
		done_chan <- 1
	}()

	for i := 0; i < 20; i++ {
		fmt.Printf("Generating executor %d\n", i)
		exec := NewStringPrintExecutor(
			fmt.Sprintf("executor %d", i))
		exec_channel <- exec
	}

	notify_chan := make(chan int)
	// Executor just emits on channel.
	notify_exec_1 := NewNotifyExecutor(notify_chan)

	not_2 := make(chan int)
	// Executor just emits on channel.
	not_exec_2 := NewNotifyExecutor(not_2)

	exec_channel <- notify_exec_1
	exec_channel <- not_exec_2

	close(exec_channel)

	fmt.Println("Waiting")

	<- notify_chan
	fmt.Println("Channel 1 done")
	<- not_2
	fmt.Println("Channel 2 done")

	<- done_chan
	fmt.Println("done")
}
